/*
Copyright 2017 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "seurat/ingest/ldi_loader.h"

#include <memory>
#include <string>

#include "ion/math/vector.h"
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "seurat/api/image.pb.h"
#include "seurat/base/file_system.h"
#include "seurat/base/status.h"
#include "seurat/image/ldi.h"
#include "seurat/testing/ion_test_utils.h"
#include "seurat/testing/test_flags.h"

namespace seurat {
namespace ingest {
namespace {

using base::Color4f;
using ion::math::Point2i;
using ion::math::Vector2i;

TEST(LdiLoader, ReadArnoldExr) {
  // The scene in the test file consists of:
  //
  // * An opaque red plane at world-space Z position -2.0.
  // * A semi-transparent (opacity 0.5) blue plane at world-space Z position
  //   -1.0.
  // * A camera at the origin, looking down the negative Z-axis, with an aspect
  //   ratio of 2.0 and a vertical FOV of 90 degree.
  //
  // The image resolution is 64 x 32 pixels. Note that Arnold renders depth in
  // eye-space Z, so expected depth values are 1.0 and 2.0, respectively.  The
  // color channels use premultiplied alpha.
  //
  // Values read from the test file may be off by some epsilon, the numerical
  // values are generated by a raytracer, and hence some floating-point
  // precision issues are to be expected.
  base::FileSystem file_system(seurat::testing::GetTestSrcdir());
  const char kExrFilename[] =
      "com_google_seurat/seurat/ingest/testdata/arnold_deep_exr.exr";
  api::proto::Ldi ldi_proto;
  api::proto::LdiFile* const ldi_file_proto = ldi_proto.mutable_ldi_file();
  ldi_file_proto->set_path(kExrFilename);

  auto ldi_loader = absl::make_unique<LdiLoader>();
  image::Ldi4f ldi;
  EXPECT_TRUE(ldi_loader->Load(ldi_proto, &file_system, &ldi).ok());

  const Vector2i kSize(64, 32);
  const Color4f kOpaqueRed(1.0f, 0.0f, 0.0f, 1.0f);
  // Arnold uses premultiplied alpha for DeepEXR output.
  const Color4f kSemiTransparentBlue(0.0f, 0.0f, 0.5f, 0.5f);
  EXPECT_EQ(kSize, ldi.GetSize());
  for (int y = 0; y < kSize[1]; ++y) {
    for (int x = 0; x < kSize[0]; ++x) {
      const Point2i p(x, y);
      EXPECT_EQ(2, ldi.GetSampleCount(p));
      EXPECT_VECTOR_NEAR(kSemiTransparentBlue, ldi.GetColors(p)[0], 1e-3);
      EXPECT_FLOAT_EQ(1.0f, ldi.GetDepths(p)[0]);
      EXPECT_VECTOR_NEAR(kOpaqueRed, ldi.GetColors(p)[1], 1e-3);
      EXPECT_FLOAT_EQ(2.0f, ldi.GetDepths(p)[1]);
    }
  }
}

TEST(LdiLoader, DepthImageWithTransparency) {
  // Test with an analytically generated EXR depth image.
  //
  // The image has a size of 32x16 pixels and contains five channels:
  // "R", "G", "B", "A", and "Z".
  //
  // The upper half of the image is semi-transparent red:
  // RGBA=(0.5, 0.0, 0.0, 0.5) and Z=0.25.
  //
  // The lower half of the image is completely transparent:
  // RGBA=(0.0, 0.0, 0.0, 0.0) and Z=0.0.
  //
  // The LdiLoader is expected to output no samples for the lower half of the
  // image and to output the upper half exactly as specified above.

  base::FileSystem file_system(seurat::testing::GetTestSrcdir());
  const char kExrFilename[] =
      "com_google_seurat/seurat/ingest/testdata/depth_image_with_transparency.exr";
  api::proto::Ldi ldi_proto;
  api::proto::DepthImageFile* const depth_image_proto =
      ldi_proto.mutable_depth_image_file();
  api::proto::Image4File* const color_proto =
      depth_image_proto->mutable_color();
  api::proto::Image1File* const depth_proto =
      depth_image_proto->mutable_depth();
  color_proto->set_path(kExrFilename);
  color_proto->set_channel_0("R");
  color_proto->set_channel_1("G");
  color_proto->set_channel_2("B");
  color_proto->set_channel_alpha("A");
  depth_proto->set_path(kExrFilename);
  depth_proto->set_channel_0("Z");

  auto ldi_loader = absl::make_unique<LdiLoader>();
  image::Ldi4f ldi;
  EXPECT_TRUE(ldi_loader->Load(ldi_proto, &file_system, &ldi).ok());

  const Vector2i kSize(32, 16);
  const Color4f kSemiTransparentRed(0.5f, 0.0f, 0.0f, 0.5f);
  const float kDepth = 0.25f;

  for (int y = 0; y < kSize[1]; ++y) {
    for (int x = 0; x < kSize[0]; ++x) {
      const Point2i p(x, y);
      if (y < kSize[1] / 2) {
        EXPECT_EQ(0, ldi.GetSampleCount(p));
      } else {
        EXPECT_EQ(1, ldi.GetSampleCount(p));
        EXPECT_VECTOR_NEAR(kSemiTransparentRed, ldi.GetColors(p)[0], 1e-3f);
        EXPECT_FLOAT_EQ(kDepth, ldi.GetDepths(p)[0]);
      }
    }
  }
}

}  // namespace
}  // namespace ingest
}  // namespace seurat
